﻿#pragma warning disable CS8618
using DotNetCommon.Extensions;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;

namespace DeepClonePerformanceTest
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var list = new List<Person>();
            for (int i = 0; i < 100; i++)
            {
                list.Add(new Person() { Id = i + 1, Name = i + 1 + "-name", Age = Random.Shared.Next(5, 10) + 15, Birth = DateTime.Parse("1995-01-02"), CreateTime = DateTime.Now, UpdateTime = DateTime.Now, Desc = i + 1 + "-desc", Phone = "027-12345678", Score = 95, State = EnumState.Normal });
            }

            //手写代码
            Console.WriteLine($"SrcClone--------------------------------List<Person>[Count=100]------------------------------------");
            TestSrcClone(list);

            //DeepClone 使用编译表达式
            Console.WriteLine();
            Console.WriteLine($"DeepClone--------------------------------List<Person>[Count=100]------------------------------------");
            TestDeepClone(list, false);

            //json 序列化反序列化
            Console.WriteLine();
            Console.WriteLine($"JsonSerial--------------------------------List<Person>[Count=100]------------------------------------");
            TestJsonSerial(list);

            //BinarySerialize 序列化反序列化
            Console.WriteLine();
            Console.WriteLine($"BinarySerial--------------------------------List<Person>[Count=100]------------------------------------");
            TestBinarySerial(list);

            Console.WriteLine("ok");
            Console.ReadLine();
        }

        static void TestSrcClone(List<Person> list)
        {
            list.Select(i => new Person { Id = i.Id, Age = i.Age, Birth = i.Birth, CreateTime = i.CreateTime, Desc = i.Desc, Name = i.Name, Phone = i.Phone, Score = i.Score, State = i.State, UpdateTime = i.UpdateTime }).ToList();
            var st = new Stopwatch();
            st.Start();
            var newList = list.Select(i => new Person { Id = i.Id, Age = i.Age, Birth = i.Birth, CreateTime = i.CreateTime, Desc = i.Desc, Name = i.Name, Phone = i.Phone, Score = i.Score, State = i.State, UpdateTime = i.UpdateTime }).ToList();
            st.Stop();
            Console.WriteLine($"1 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 100; i++)
            {
                var newList2 = list.Select(i => new Person { Id = i.Id, Age = i.Age, Birth = i.Birth, CreateTime = i.CreateTime, Desc = i.Desc, Name = i.Name, Phone = i.Phone, Score = i.Score, State = i.State, UpdateTime = i.UpdateTime }).ToList();
            }
            st.Stop();
            Console.WriteLine($"100 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (100 * 1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 1000; i++)
            {
                var newList2 = list.Select(i => new Person { Id = i.Id, Age = i.Age, Birth = i.Birth, CreateTime = i.CreateTime, Desc = i.Desc, Name = i.Name, Phone = i.Phone, Score = i.Score, State = i.State, UpdateTime = i.UpdateTime }).ToList();
            }
            st.Stop();
            Console.WriteLine($"1000 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (1000 * 1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 10000; i++)
            {
                var newList2 = list.Select(i => new Person { Id = i.Id, Age = i.Age, Birth = i.Birth, CreateTime = i.CreateTime, Desc = i.Desc, Name = i.Name, Phone = i.Phone, Score = i.Score, State = i.State, UpdateTime = i.UpdateTime }).ToList();
            }
            st.Stop();
            Console.WriteLine($"10000 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (10000 * 1.0)} 毫秒");

            //st.Restart();
            //for (int i = 0; i < 10000 * 10; i++)
            //{
            //    var newList2 = list.Select(i => new Person { Id = i.Id, Age = i.Age, Birth = i.Birth, CreateTime = i.CreateTime, Desc = i.Desc, Name = i.Name, Phone = i.Phone, Score = i.Score, State = i.State, UpdateTime = i.UpdateTime }).ToList();
            //}
            //st.Stop();
            //Console.WriteLine($"10000*10 次克隆 100 条数据,耗时: {st.ElapsedMilliseconds} 毫秒，平均: {st.ElapsedMilliseconds / (10000 * 10 * 1.0)} 毫秒");
        }

        static void TestDeepClone(List<Person> list, bool containsRepeatReference = false)
        {
            list.DeepClone();
            var st = new Stopwatch();
            st.Start();
            var newList = list.DeepClone(containsRepeatReference);
            st.Stop();
            Console.WriteLine($"1 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 100; i++)
            {
                var newList2 = list.DeepClone(containsRepeatReference);
            }
            st.Stop();
            Console.WriteLine($"100 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (100 * 1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 1000; i++)
            {
                var newList2 = list.DeepClone(containsRepeatReference);
            }
            st.Stop();
            Console.WriteLine($"1000 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (1000 * 1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 10000; i++)
            {
                var newList2 = list.DeepClone(containsRepeatReference);
            }
            st.Stop();
            Console.WriteLine($"10000 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (10000 * 1.0)} 毫秒");

            //st.Restart();
            //for (int i = 0; i < 10000 * 10; i++)
            //{
            //    var newList2 = list.DeepClone(false);
            //}
            //st.Stop();
            //Console.WriteLine($"10000*10 次克隆 100 条数据,耗时: {st.ElapsedMilliseconds} 毫秒，平均: {st.ElapsedMilliseconds / (10000 * 10 * 1.0)} 毫秒");
        }

        static void TestJsonSerial(List<Person> list)
        {
            Newtonsoft.Json.JsonConvert.DeserializeObject<List<Person>>(Newtonsoft.Json.JsonConvert.SerializeObject(list));
            var st = new Stopwatch();
            st.Start();
            var newList = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Person>>(Newtonsoft.Json.JsonConvert.SerializeObject(list));
            st.Stop();
            Console.WriteLine($"1 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 100; i++)
            {
                var newList2 = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Person>>(Newtonsoft.Json.JsonConvert.SerializeObject(list));
            }
            st.Stop();
            Console.WriteLine($"100 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (100 * 1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 1000; i++)
            {
                var newList2 = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Person>>(Newtonsoft.Json.JsonConvert.SerializeObject(list));
            }
            st.Stop();
            Console.WriteLine($"1000 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t平均: {st.ElapsedMilliseconds / (1000 * 1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 10000; i++)
            {
                var newList2 = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Person>>(Newtonsoft.Json.JsonConvert.SerializeObject(list));
            }
            st.Stop();
            Console.WriteLine($"10000 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t平均: {st.ElapsedMilliseconds / (10000 * 1.0)} 毫秒");

            //st.Restart();
            //for (int i = 0; i < 10000 * 10; i++)
            //{
            //    var newList2 = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Person>>(Newtonsoft.Json.JsonConvert.SerializeObject(list));
            //}
            //st.Stop();
            //Console.WriteLine($"10000*10 次克隆 100 条数据,耗时: {st.ElapsedMilliseconds} 毫秒，平均: {st.ElapsedMilliseconds / (10000 * 10 * 1.0)} 毫秒");
        }

        static void TestBinarySerial(List<Person> list)
        {
            BinaryFormatter bf = new BinaryFormatter();
            var stream = new MemoryStream();
            bf.Serialize(stream, list);
            var bs = new byte[stream.Length];
            stream.Close();
            stream = new MemoryStream(bs);
            bf.Serialize(stream, list);
            stream.Seek(0, SeekOrigin.Begin);
            bf.Deserialize(stream);
            stream.Close();

            var st = new Stopwatch();
            st.Start();
            stream = new MemoryStream(bs);
            bf.Serialize(stream, list);
            stream.Seek(0, SeekOrigin.Begin);
            var newList = bf.Deserialize(stream) as List<Person>;
            stream.Close();
            st.Stop();
            Console.WriteLine($"1 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 100; i++)
            {
                stream = new MemoryStream(bs);
                bf.Serialize(stream, list);
                stream.Seek(0, SeekOrigin.Begin);
                var newList2 = bf.Deserialize(stream) as List<Person>;
                stream.Close();
            }
            st.Stop();
            Console.WriteLine($"100 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t\t平均: {st.ElapsedMilliseconds / (100 * 1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 1000; i++)
            {
                stream = new MemoryStream(bs);
                bf.Serialize(stream, list);
                stream.Seek(0, SeekOrigin.Begin);
                var newList2 = bf.Deserialize(stream) as List<Person>;
                stream.Close();
            }
            st.Stop();
            Console.WriteLine($"1000 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t平均: {st.ElapsedMilliseconds / (1000 * 1.0)} 毫秒");

            st.Restart();
            for (int i = 0; i < 10000; i++)
            {
                stream = new MemoryStream(bs);
                bf.Serialize(stream, list);
                stream.Seek(0, SeekOrigin.Begin);
                var newList2 = bf.Deserialize(stream) as List<Person>;
                stream.Close();
            }
            st.Stop();
            Console.WriteLine($"10000 次克隆\t\t\t耗时: {st.ElapsedMilliseconds} 毫秒，\t\t平均: {st.ElapsedMilliseconds / (10000 * 1.0)} 毫秒");

            //st.Restart();
            //for (int i = 0; i < 10000 * 10; i++)
            //{
            //    stream = new MemoryStream(bs);
            //    bf.Serialize(stream, list);
            //    stream.Seek(0, SeekOrigin.Begin);
            //    var newList2 = bf.Deserialize(stream) as List<Person>;
            //    stream.Close();
            //}
            //st.Stop();
            //Console.WriteLine($"10000*10 次克隆 100 条数据,耗时: {st.ElapsedMilliseconds} 毫秒，平均: {st.ElapsedMilliseconds / (10000 * 10 * 1.0)} 毫秒");
        }
    }

    [Serializable]
    class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public DateTime Birth { get; set; }
        public double Score { get; set; }
        public DateTime CreateTime { get; set; }
        public DateTime UpdateTime { get; set; }
        public EnumState State { get; set; }
        public string Desc { get; set; }
        public string Phone { get; set; }
    }

    enum EnumState { Normal, Freeze }
}